package main;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.print.Doc;
import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.SwingConstants;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
/**
 * GUI creates the UI for the webCrawler.
 * @version 5-11-2012
 * @author T. Lewis & Ken Norton BitWiseGuys.
 * */
public class GUI implements Observer {

	private static final Integer[] THREADS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; 

	private String URL = "";

	private JFrame my_frame;

	private JTextField url_text;

	private JList word_list;

	private JTextField word_text;

	private DefaultListModel dlm;

	private JCheckBox multi;

	private JTextField page_count;

	private JComboBox ret_threads;

	private JComboBox parser_threads;

	private JTextPane output_text;

	private JTextField file_text;

	private StyledDocument doc;

	private Style my_style;

	private JTextField runningtime;

	private Thread t;

	private JButton start_btn;

	private Spider spider;

	/**
	 * Instantiates the GUI class and creates the main frame.
	 */
	public GUI() {
		createFrame();
	}

	/**
	 * Creates the main frame.
	 */
	private void createFrame() {
		my_frame = new JFrame("WebCrawler");

		// Size and position
		Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
		my_frame.setSize(600, 500);
		my_frame.setLayout(new BorderLayout());
		int x = (screen.width - my_frame.getWidth()) / 2;
		int y = (screen.height - my_frame.getHeight()) / 2;
		my_frame.setLocation(x, y);
		my_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		my_frame.setResizable(false);

		southPanel();

		northPanel();

		westPanel();

		eastPanel();

		my_frame.setVisible(true);
	}

	/**
	 * Sets up the north panel and adds it to the main frame.
	 */
	private void northPanel() {
		// URL Text field
		JPanel north_panel = new JPanel(new BorderLayout());

		JPanel url_panel = new JPanel();
		GroupLayout layout = new GroupLayout(url_panel);
		url_panel.setLayout(layout);

		JLabel url_label = new JLabel("Seed URL: ");
		JLabel file_label = new JLabel("Words file: ");
		JButton browse_button = new JButton("Browse");

		browse_button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent the_event) {
				JFileChooser fc = new JFileChooser();
				fc.setFileFilter(new FileFilter() {

					@Override
					public String getDescription() {
						return "Text Files";
					}

					@Override
					public boolean accept(File f) {
						return f.getName().endsWith(".txt");
					}
				});

				int returnval = fc.showDialog(my_frame, "Choose");

				if (returnval == JFileChooser.APPROVE_OPTION) {
					file_text.setText(fc.getSelectedFile().getPath());
				}
			}
		});

		url_text = new JTextField(20);
		file_text = new JTextField();

		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);

		layout.setHorizontalGroup(layout.createParallelGroup()
				.addGroup(layout.createSequentialGroup()
						.addComponent(url_label)
						.addComponent(url_text)
						)
						.addGroup(layout.createSequentialGroup()
								.addComponent(file_label)
								.addComponent(file_text)
								.addComponent(browse_button)
								)
				);

		layout.setVerticalGroup(layout.createSequentialGroup()
				.addGroup(layout.createParallelGroup()
						.addComponent(url_label)
						.addComponent(url_text)
						)
						.addGroup(layout.createParallelGroup()
								.addComponent(file_label)
								.addComponent(file_text)
								.addComponent(browse_button)
								)
				);

		north_panel.add(url_panel, BorderLayout.WEST);
		my_frame.add(north_panel, BorderLayout.NORTH);

		// Thread dropdowns

		// Retriever
		JPanel thread_panel = new JPanel(new BorderLayout());
		JPanel ret_panel = new JPanel();
		JLabel ret_label = new JLabel("Retriever threads:");
		ret_threads = new JComboBox(THREADS);

		ret_panel.add(ret_label);
		ret_panel.add(ret_threads);
		thread_panel.add(ret_panel, BorderLayout.NORTH);

		// Parser
		JPanel parser_panel = new JPanel();
		JLabel parser_label = new JLabel("Parser threads:");
		parser_threads = new JComboBox(THREADS);

		parser_panel.add(parser_label);
		parser_panel.add(parser_threads);
		thread_panel.add(parser_panel, BorderLayout.SOUTH);

		north_panel.add(thread_panel, BorderLayout.EAST);
	}

	/**
	 * Sets up the south panel and adds it to the main frame.
	 */
	private void southPanel() {
		// Output text area
		JLabel out_label = new JLabel("Output: ");

		JPanel south_panel = new JPanel();
		output_text = new JTextPane();
		output_text.setEditable(false);
		doc = output_text.getStyledDocument();
		my_style = doc.addStyle("myStyle", null);
		Font font = new Font("Monospaced", Font.PLAIN, 14);
		StyleConstants.setFontFamily(my_style, "Monospaced");
		StyleConstants.setFontSize(my_style, 12);

		JScrollPane jsp = new JScrollPane(output_text);
		jsp.setPreferredSize(new Dimension(500, 200));
		jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

		my_frame.add(south_panel, BorderLayout.SOUTH);
		south_panel.add(out_label);
		south_panel.add(jsp);
	}

	/**
	 * Sets up the west panel and adds it to the main frame.
	 */
	private void westPanel() {
		// Keyword list box.
		JLabel label = new JLabel("Enter key words: ");
		JButton clear = new JButton("Clear fields");
		dlm = new DefaultListModel();
		word_list = new JList(dlm);

		JScrollPane scroller = new JScrollPane(word_list);
		scroller.setPreferredSize(new Dimension(200, 5));

		word_text = new JTextField();
		word_text.setColumns(1);

		JButton add = new JButton("Add word");
		JButton remove = new JButton("Remove word(s)");
		JButton file_vals = new JButton("Load from file");

		add.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if(word_text.getText().isEmpty()){
					JOptionPane.showMessageDialog(my_frame, "Word to Add is empty Please enter a valid value.");
					word_text.requestFocusInWindow();
					return;
				}else{
					//Clean the String no spaces or uppercase
					String tempWord = word_text.getText();
					tempWord = tempWord.trim();
					tempWord = tempWord.toLowerCase();
					if (!dlm.contains(tempWord)) {

						dlm.addElement(tempWord);
					}else{
						JOptionPane.showMessageDialog(my_frame, "That word has already been entered! \n" +
								"Please enter a valid value.");
						word_text.requestFocusInWindow();
						return;
					}
				}
			}
		});

		remove.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				@SuppressWarnings("unchecked")
				int selected[] = word_list.getSelectedIndices();

				for (int i = 0; i < selected.length; i++) {
					dlm.remove(selected[i] - i);
				}
			}
		});

		file_vals.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				loadValues();
			}
		});

		clear.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				dlm.removeAllElements();
				word_text.setText("");
				url_text.setText("");
			}
		});

		JPanel west_panel = new JPanel();
		GroupLayout layout = new GroupLayout(west_panel);
		west_panel.setLayout(layout);

		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);

		layout.setHorizontalGroup(layout.createSequentialGroup()
				.addGroup(layout.createParallelGroup()
						.addComponent(label)
						.addComponent(word_text)
						.addComponent(scroller))
						.addGroup(layout.createParallelGroup()
								.addComponent(file_vals)
								.addComponent(add)
								.addComponent(remove)
								.addComponent(clear)
								)
				);

		layout.linkSize(SwingConstants.HORIZONTAL, word_text, scroller);
		layout.linkSize(SwingConstants.HORIZONTAL, add, remove, file_vals, clear);
		layout.linkSize(SwingConstants.VERTICAL, word_text, add);

		layout.setVerticalGroup(layout.createParallelGroup()
				.addGroup(layout.createSequentialGroup()
						.addComponent(label)
						.addComponent(word_text)
						.addComponent(scroller))
						.addGroup(layout.createSequentialGroup()
								.addComponent(file_vals)
								.addComponent(add)
								.addComponent(remove)
								.addComponent(clear)
								)
				);

		my_frame.add(west_panel, BorderLayout.WEST);
	}

	/**
	 * Creates the east panel and adds it to the main frame.
	 */
	private void eastPanel() {
		start_btn = new JButton("Start crawl");
		multi = new JCheckBox("Multithreaded");
		multi.setSelected(true);
		page_count = new JTextField();
		page_count.setColumns(15);
		JLabel time_label = new JLabel("Total crawl time: ");
		runningtime = new JTextField();

		JLabel label = new JLabel("Page count: ");

		multi.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				ret_threads.setEnabled(multi.isSelected());
				parser_threads.setEnabled(multi.isSelected());
			}
		});

		start_btn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				runningtime.setText("");

				output_text.setText("");
				String url = url_text.getText();

				if (url.isEmpty()){
					JOptionPane.showMessageDialog(my_frame, "Url is empty Please enter a valid value.");
					url_text.requestFocusInWindow();
					return;
				}

				// Get word list 
				String[] list = new String[dlm.getSize()];
				for (int i = 0; i < dlm.getSize(); i++) {
					list[i] = (String) dlm.elementAt(i);
				}

				// Get page count
				String count = page_count.getText();
				int max = 0;
				try {
					max = Integer.parseInt(count);
				} catch (NumberFormatException e) {
					JOptionPane.showMessageDialog(my_frame, "Enter a positive integer for page count.");
					page_count.requestFocusInWindow();
				}

				if (max > 0) {
					if (multi.isSelected()) {
						int ret = ((Integer) ret_threads.getSelectedItem()).intValue();
						int par = ((Integer) parser_threads.getSelectedItem()).intValue();
						spider = new Spider(url, list, ret, par, max, multi.isSelected());
					} else {
						spider = new Spider(url, list, 1, 1, max, multi.isSelected());
					}

					spider.addObserver(GUI.this);
					t = new Thread(spider);

					t.start();

					if (t != null && t.isAlive()) {
						((JButton) event.getSource()).setEnabled(false);
						Thread monitor = new Thread(new Runnable() {
							@Override
							public void run() {
								while (GUI.this.t.isAlive())
									;
								GUI.this.start_btn.setEnabled(true);

								DecimalFormat df = new DecimalFormat("#,##0.000");
								runningtime.setText(df.format(GUI.this.spider.getRunTime()));
								runningtime.selectAll();
								Toolkit.getDefaultToolkit().beep();
							}
						});
						monitor.start();
					}
				}
			}
		});

		JPanel panel = new JPanel();

		GroupLayout layout = new GroupLayout(panel);
		panel.setLayout(layout);
		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);

		layout.setHorizontalGroup(layout.createParallelGroup()
				.addComponent(multi)
				.addComponent(label)
				.addComponent(page_count)
				.addComponent(start_btn)
				.addComponent(time_label)
				.addComponent(runningtime)
				);

		layout.linkSize(start_btn, page_count);

		layout.setVerticalGroup(layout.createSequentialGroup()
				.addComponent(multi)
				.addComponent(label)
				.addComponent(page_count)
				.addComponent(start_btn)
				.addComponent(time_label)
				.addComponent(runningtime)
				);

		my_frame.add(panel, BorderLayout.EAST);
	}

	/**
	 * Resets the default values.
	 */
	public void loadValues() {
		dlm.removeAllElements();

		List<String> list = getWordsFromFile(file_text.getText());

		if (list != null) {
			for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
				String tempString = iterator.next();
				tempString = tempString.trim();
				tempString = tempString.toLowerCase();
				dlm.addElement((String) tempString);
			}
		}

		url_text.setText(URL);
	}

	/**
	 * Gets the frame for the GUI.
	 * 
	 * @return The frame for the GUI.
	 */
	public JFrame getFrame() {
		return my_frame;
	}

	private List<String> getWordsFromFile(final String the_file) {
		List<String> list = new ArrayList<String>();
		String str = "";
		String msg = "";

		BufferedReader br = null;

		try {
			br = new BufferedReader(new InputStreamReader(new DataInputStream(new FileInputStream(the_file))));

			while ((str = br.readLine()) != null) {
				if (str.startsWith("url:")) {
					URL = str.trim();
				} else {
					list.add(str.trim());
				}
			}
		} catch (final FileNotFoundException the_exception) {
			msg = "The file was not found.";
		} catch (final IOException the_exception) {
			msg = "The file could not be read.";
		}

		if (msg != "") {
			JOptionPane.showMessageDialog(my_frame, msg);
		}

		return list;
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		if (arg1 instanceof List<?>) {
			List<String> list = (List<String>) arg1;
			for (Iterator<String> iterator = list.iterator(); iterator
					.hasNext();) {
				try {
					doc.insertString(doc.getLength(), iterator.next() + "\n",
							my_style);
				} catch (BadLocationException e) {

				}
			}
		} else if (arg1 instanceof Spider){
			Spider sp = (Spider) arg1;
		}
	}
}
